View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2000-2005 (C) Exoffice Technologies Inc. All Rights Reserved.
42   */
43  package org.exolab.jms.persistence;
44  
45  import java.sql.Connection;
46  import java.sql.PreparedStatement;
47  import java.sql.ResultSet;
48  import java.util.HashMap;
49  import java.util.Iterator;
50  import java.util.Vector;
51  
52  import org.exolab.jms.client.JmsDestination;
53  import org.exolab.jms.client.JmsQueue;
54  import org.exolab.jms.client.JmsTopic;
55  
56  
57  /***
58   * This class provides persistency for JmsDestination objects in an RDBMS
59   * database.
60   *
61   * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
62   * @version $Revision: 1.4 $ $Date: 2005/08/31 05:45:50 $
63   */
64  class Destinations {
65  
66      /***
67       * The seed generator.
68       */
69      private final SeedGenerator _seeds;
70  
71      /***
72       * The consumer manager.
73       */
74      private final Consumers _consumers;
75  
76      /***
77       * Cache of all destinations indexed on names.
78       */
79      private final HashMap _destinations = new HashMap();
80  
81      /***
82       * Cache of all destinations indexed on identity.
83       */
84      private final HashMap _ids = new HashMap();
85  
86      /***
87       * This is the name of the destination id generator, which uniquely created
88       * identities for destinations.
89       */
90      private static final String DESTINATION_ID_SEED = "destinationId";
91  
92      /***
93       * Create a new <code>Destinations</code>.
94       *
95       * @param seeds the seed manager
96       * @param consumers the consumer manager
97       * @param connection the connection to initialise from
98       * @throws PersistenceException if initialisation fails
99       */
100     public Destinations(SeedGenerator seeds,
101                         Consumers consumers,
102                         Connection connection) throws PersistenceException {
103         _seeds = seeds;
104         _consumers = consumers;
105         load(connection);
106     }
107 
108     /***
109      * Add a new destination to the database. This method will also assign it a
110      * unique identity.
111      *
112      * @param connection  the connection to use.
113      * @param destination the destination to add
114      * @throws PersistenceException if the destination cannot be added
115      */
116     public synchronized void add(Connection connection,
117                                  JmsDestination destination)
118             throws PersistenceException {
119 
120         PreparedStatement insert = null;
121         try {
122             long Id = _seeds.next(connection, DESTINATION_ID_SEED);
123             boolean isQueue = (destination instanceof JmsQueue);
124 
125             insert = connection.prepareStatement(
126                     "insert into destinations (name, isqueue, destinationid) "
127                     + "values (?, ?, ?)");
128             insert.setString(1, destination.getName());
129             insert.setBoolean(2, isQueue);
130             insert.setLong(3, Id);
131             insert.executeUpdate();
132             cache(destination, Id);
133         } catch (Exception error) {
134             throw new PersistenceException("Destinations.add failed with " +
135                                            error.toString());
136         } finally {
137             SQLHelper.close(insert);
138         }
139     }
140 
141     /***
142      * Remove a destination from the database. This removes all associated
143      * consumers, and messages.
144      *
145      * @param connection  the connection to use
146      * @param destination the destination
147      * @return <code>true</cpde> if it was removed
148      * @throws PersistenceException if the request fails
149      */
150     public synchronized boolean remove(Connection connection,
151                                        JmsDestination destination)
152             throws PersistenceException {
153 
154         boolean success = false;
155         PreparedStatement deleteDestinations = null;
156         PreparedStatement deleteMessages = null;
157         PreparedStatement deleteConsumers = null;
158         PreparedStatement deleteMessageHandles = null;
159 
160         Pair pair = (Pair) _destinations.get(destination.getName());
161         if (pair != null) {
162             try {
163                 deleteDestinations = connection.prepareStatement(
164                         "delete from destinations where name=?");
165                 deleteDestinations.setString(1, destination.getName());
166 
167                 deleteMessages = connection.prepareStatement(
168                         "delete from messages where destinationId=?");
169                 deleteMessages.setLong(1, pair.Id);
170 
171                 deleteMessageHandles = connection.prepareStatement(
172                         "delete from message_handles where destinationId=?");
173                 deleteMessageHandles.setLong(1, pair.Id);
174 
175                 deleteConsumers = connection.prepareStatement(
176                         "delete from consumers where destinationId=?");
177                 deleteConsumers.setLong(1, pair.Id);
178 
179 
180                 deleteDestinations.executeUpdate();
181                 deleteMessages.executeUpdate();
182                 deleteMessageHandles.executeUpdate();
183                 deleteConsumers.executeUpdate();
184 
185                 _consumers.removeCached(pair.Id);
186                 _destinations.remove(destination.getName());
187                 _ids.remove(new Long(pair.Id));
188                 success = true;
189             } catch (Exception error) {
190                 throw new PersistenceException("Failed to remove destination",
191                                                error);
192             } finally {
193                 SQLHelper.close(deleteDestinations);
194                 SQLHelper.close(deleteMessages);
195                 SQLHelper.close(deleteConsumers);
196                 SQLHelper.close(deleteMessageHandles);
197             }
198         }
199 
200         return success;
201     }
202 
203     /***
204      * Returns the destination associated with name.
205      *
206      * @param name the name of the destination
207      * @return the destination, or null
208      */
209     public synchronized JmsDestination get(String name) {
210         Pair pair = (Pair) _destinations.get(name);
211         return (pair != null) ? pair.destination : null;
212     }
213 
214     /***
215      * Returns the destination associated with Id.
216      *
217      * @param id the destination Id
218      * @return the destination or null
219      */
220     public synchronized JmsDestination get(long id) {
221         Pair pair = (Pair) _ids.get(new Long(id));
222         return (pair != null) ? pair.destination : null;
223     }
224 
225     /***
226      * Returns the Id for a given destination name
227      *
228      * @param name the destination name
229      * @return the destination Id, or 0 if it doesn't exist
230      */
231     public synchronized long getId(String name) {
232         Pair pair = (Pair) _destinations.get(name);
233         return (pair != null) ? pair.Id : 0;
234     }
235 
236     /***
237      * Returns the list of destination names.
238      *
239      * @return list of destination names
240      */
241     public synchronized Vector getNames() {
242         // return a Vector for legacy reasons.
243         Vector result = new Vector(_destinations.size());
244         Iterator iter = _destinations.keySet().iterator();
245         while (iter.hasNext()) {
246             result.add((String) iter.next());
247         }
248 
249         return result;
250     }
251 
252     /***
253      * Returns the list of destination objects.
254      *
255      * @return list of destination objects
256      */
257     public synchronized Vector getDestinations() {
258         // return a Vector for legacy reasons.
259         Vector result = new Vector(_destinations.size());
260         Iterator iter = _destinations.values().iterator();
261         while (iter.hasNext()) {
262             result.add(((Pair) iter.next()).destination);
263         }
264 
265         return result;
266     }
267 
268     /***
269      * Deallocates resources owned or referenced by the instance.
270      */
271     public synchronized void close() {
272         _destinations.clear();
273         _ids.clear();
274     }
275 
276     /***
277      * Load all the destinations in memory. It uses the transaction service and
278      * the database service to access the appropriate resources.
279      *
280      * @param connection the connection to use
281      * @throws PersistenceException if the
282      */
283     private void load(Connection connection)
284             throws PersistenceException {
285 
286         PreparedStatement select = null;
287         ResultSet set = null;
288         try {
289             select = connection.prepareStatement(
290                     "select name, isqueue, destinationid from destinations");
291 
292             set = select.executeQuery();
293             String name = null;
294             boolean isQueue = false;
295             JmsDestination destination = null;
296             long Id = 0;
297             while (set.next()) {
298                 name = set.getString(1);
299                 isQueue = set.getBoolean(2);
300                 destination = (isQueue)
301                         ? (JmsDestination) new JmsQueue(name)
302                         : (JmsDestination) new JmsTopic(name);
303                 Id = set.getLong(3);
304                 destination.setPersistent(true);
305                 cache(destination, Id);
306             }
307         } catch (Exception exception) {
308             throw new PersistenceException("Failed to load destinations",
309                                            exception);
310         } finally {
311             SQLHelper.close(set);
312             SQLHelper.close(select);
313         }
314     }
315 
316     /***
317      * Cache a destination.
318      *
319      * @param destination the destination to cache
320      * @param Id          the destination identity
321      */
322     private void cache(JmsDestination destination, long Id) {
323         Pair pair = new Pair(destination, Id);
324 
325         _destinations.put(destination.getName(), pair);
326         _ids.put(new Long(Id), pair);
327     }
328 
329 
330     /***
331      * This private static class holds the name and identity of the destination
332      */
333     private static class Pair {
334 
335         public Pair(JmsDestination destination, long Id) {
336             this.destination = destination;
337             this.Id = Id;
338         }
339 
340         public JmsDestination destination;
341         public long Id;
342     }
343 }